/* Orbas:
 *     A open source CORBA Specification implementation from Huihoo.
 *
 * Copyright 2002-2003 Huihoo.org, Inc. All Right Reserved.
 *
 * This software is licensed under LGPL license.
 * See terms of license at gnu.org.
 *
 * For more information, visit:
 *
 * http://www.huihoo.org/orbas
 */

package org.huihoo.orbas.orb.cdr;

/**
 * <p>Description: </p>
 * CDR decode stream.
 * <p>Copyright (c) 2002,2003</p>
 * <p>Company: <a href="http://www.huihoo.org/">huihoo.org</a></p>
 * @author <a href="http://www.huihoo.org/~mep">mep(mep@huihoo.com)</a>
 * @see <a href="http://www.huihoo.org/orbas">http://www.huihoo.org/orbas</a>
 * @version 1.0
 */

import org.omg.CORBA.TCKind;

public class CDRInputStream extends org.omg.CORBA_2_3.portable.InputStream {
	protected byte[] buffer;
	protected int bufferLength;
	protected int nextByteToRead;
	public boolean littleEndian;
	public org.huihoo.orbas.orb.ORB orb;
	int mark;

	private java.util.Hashtable valueList;
	private int indirection;
	private java.util.Vector graph;

	public CDRInputStream(org.huihoo.orbas.orb.ORB orb, byte[] buf) {
		this.orb = orb;
		this.buffer = buf;
		this.nextByteToRead = 0;
		this.bufferLength = buf.length;
		this.mark = 0;
		this.littleEndian = false;

		this.valueList = new java.util.Hashtable();
		this.graph = new java.util.Vector();
	}

	public CDRInputStream(byte[] buf) {
		this(null, buf);
	}

	public boolean markSupported() {
		return true;
	}

	public void mark() {
		mark = nextByteToRead;
	}

	public void reset() {
		nextByteToRead = mark;
	}

	public CDRInputStream read_encapsulation() {
		// mark();
		int length = this.read_long();
		// reset();
		byte[] value = new byte[length];
		System.arraycopy(buffer, 0, value, 0, length);

		return new CDRInputStream(this.orb, value);
	}

	public final byte read_octet() {
		return buffer[nextByteToRead++];
	}

	public final void read_octet_array(byte[] value, int offset, int length) {
		for (int i = 0; i < length; i++) {
			value[i + offset] = read_octet();
		}
	}

	public final boolean read_boolean() {
		return read_octet() != 0;
	}

	public final void read_boolean_array(boolean[] value, int offset, int length) {
		for (int i = 0; i < length; i++) {
			value[i + offset] = read_boolean();
		}
	}

	public final char read_char() {
		// here for eliminate of the hi byte of char
		return (char) (((char) read_octet()) & 0xFF);
	}

	public final void read_char_array(char[] value, int offset, int length) {
		for (int i = 0; i < length; i++) {
			value[i + offset] = read_char();
		}
	}

	public String read_string() {
		int length = read_long();

		if (length <= 0) {
			throw new org.omg.CORBA.MARSHAL("Invalid string length");
		}

		byte[] ret = new byte[length - 1];

		for (int i = 0; i < length - 1; i++) {
			ret[i] = read_octet();
		}
		read_octet();
		return new String(ret);
	}

	public final double read_double() {
		return Double.longBitsToDouble(read_longlong());
	}

	public final void read_double_array(double[] value, int offset, int length) {
		for (int i = 0; i < length; i++) {
			value[i + offset] = read_double();
		}
	}

	public final float read_float() {
		return Float.intBitsToFloat(read_long());
	}

	public final void read_float_array(float[] value, int offset, int length) {
		for (int i = 0; i < length; i++) {
			value[i + offset] = read_float();
		}
	}

	public final int read_long() {
		alignment(4);
		int first = read_octet();
		int second = read_octet();
		int third = read_octet();
		int fourth = read_octet();

		if (littleEndian) {
			return (fourth & 0xFF) << 24 | (third & 0xFF) << 16
					| (second & 0xFF) << 8 | (first & 0xFF);
		} else {
			return (first & 0xFF) << 24 | (second & 0xFF) << 16
					| (third & 0xFF) << 8 | (fourth & 0xFF);
		}
	}

	public final void read_long_array(int[] value, int offset, int length) {
		for (int i = 0; i < length; i++) {
			value[i + offset] = read_long();
		}
	}

	public final long read_longlong() {
		alignment(8);

		long first = (long) read_long();
		long second = (long) read_long();

		if (littleEndian) {
			return (second << 32) | (first & 0xFFFFFFFFL);
		} else {
			return (first << 32) | (second & 0xFFFFFFFFL);
		}
	}

	public final void read_longlong_array(long[] value, int offset, int length) {
		for (int i = 0; i < length; i++) {
			value[i + offset] = read_longlong();
		}
	}

	public final short read_short() {
		alignment(2);
		byte first = read_octet();
		byte second = read_octet();

		if (littleEndian) {
			return (short) (((second & 0xff) << 8) | ((first & 0xff) << 0));
		} else {
			return (short) (((first & 0xff) << 8) | ((second & 0xff) << 0));
		}
	}

	public final void read_short_array(short[] value, int offset, int length) {
		for (int i = 0; i < length; i++) {
			value[i + offset] = read_short();
		}
	}

	public final char read_wchar() {
		// GIOP1.2?
		return (char) read_short();
	}

	public final void read_wchar_array(char[] value, int offset, int length) {
		for (int i = 0; i < length; i++) {
			value[i + offset] = read_wchar();
		}
	}

	public final String read_wstring() {
		int length = read_long();

		char[] ret = new char[length - 1];

		for (int i = 0; i < length - 1; i++) {
			ret[i] = read_wchar();
		}
		read_wchar();
		return new String(ret);
	}

	public final short read_ushort() {
		return read_short();
	}

	public final void read_ushort_array(short[] value, int offset, int length) {
		for (int i = 0; i < length; i++) {
			value[i + offset] = read_ushort();
		}
	}

	public final int read_ulong() {
		return read_long();
	}

	public final void read_ulong_array(int[] value, int offset, int length) {
		for (int i = 0; i < length; i++) {
			value[i + offset] = read_ulong();
		}
	}

	public final long read_ulonglong() {
		return read_longlong();
	}

	public final void read_ulonglong_array(long[] value, int offset, int length) {
		for (int i = 0; i < length; i++) {
			value[i + offset] = read_ulonglong();
		}
	}

	public final org.omg.CORBA.Any read_any() {
		org.omg.CORBA.Any any = orb.create_any();
		org.omg.CORBA.TypeCode typecode = read_TypeCode();

		any.read_value(this, typecode);

		return any;
	}

	public final org.omg.CORBA.Object read_Object() {

		org.huihoo.orbas.orb.ior.IorParser p = new org.huihoo.orbas.orb.ior.IorParser(
				orb, this);

		return p.getObject();

	}

	public org.omg.CORBA.Object read_Object(java.lang.Class clz) {

		org.huihoo.orbas.orb.ior.IorParser p = new org.huihoo.orbas.orb.ior.IorParser(
				orb, this);

		org.omg.CORBA.Object value = p.getObject();
		org.omg.CORBA.portable.ObjectImpl objectImpl = (org.omg.CORBA.portable.ObjectImpl) value;

		if (objectImpl == null) {
			// report an error
			System.out.println("CDROutputStream: Not a CORBA Object.");
		}

		org.huihoo.orbas.orb.ObjRefDelegate delegate = (org.huihoo.orbas.orb.ObjRefDelegate) (objectImpl
				._get_delegate());

		Object stub = null;
		try {
			stub = clz.newInstance();
		} catch (InstantiationException e1) {
			System.out
					.println("CDRInputStream read_Object: Instantiation Exception");
		} catch (IllegalAccessException e2) {
			System.out
					.println("CDRInputStream read_Object: IllegalAccess Exception");
		}

		org.omg.CORBA.portable.ObjectImpl ret = (org.omg.CORBA.portable.ObjectImpl) stub;

		ret._set_delegate(delegate);

		return ret;
	}

	public org.omg.CORBA.TypeCode read_TypeCode() {
		int kind = read_long();

		switch (kind) {

		case TCKind._tk_null:
		case TCKind._tk_void:
		case TCKind._tk_short:
		case TCKind._tk_long:
		case TCKind._tk_ushort:
		case TCKind._tk_ulong:
		case TCKind._tk_float:
		case TCKind._tk_double:
		case TCKind._tk_boolean:
		case TCKind._tk_char:
		case TCKind._tk_octet:
		case TCKind._tk_any:
		case TCKind._tk_TypeCode:
		case TCKind._tk_longlong:
		case TCKind._tk_ulonglong:
		case TCKind._tk_longdouble:
		case TCKind._tk_wchar:

			return orb.get_primitive_tc(org.omg.CORBA.TCKind.from_int(kind));
			// break;

		case TCKind._tk_Principal:
			throw new org.omg.CORBA.NO_IMPLEMENT("Principal deprecated");
			// break;
		case TCKind._tk_objref:
			String objrefID = read_string();
			String objrefName = read_string();

			return orb.create_interface_tc(objrefID, objrefName);

		case TCKind._tk_enum:
			String enumId = read_string();
			String enumName = read_string();
			int enumMemCount = read_long();
			String enumMembers[] = new String[enumMemCount];

			for (int i = 0; i < enumMemCount; i++) {
				enumMembers[i] = read_string();
			}

			return orb.create_enum_tc(enumId, enumName, enumMembers);
			// break;

		case TCKind._tk_struct:
			String structId = read_string();
			String structName = read_string();
			int structMemCount = read_long();
			org.omg.CORBA.StructMember[] structMembers = new org.omg.CORBA.StructMember[structMemCount];

			for (int i = 0; i < structMemCount; i++) {
				String memberName = read_string();
				org.omg.CORBA.TypeCode type = read_TypeCode();

				structMembers[i] = new org.omg.CORBA.StructMember(memberName,
						type, null);
			}

			return orb.create_struct_tc(structId, structName, structMembers);
			// break;
		case TCKind._tk_except:
			String exceptId = read_string();
			String exceptName = read_string();
			int exceptMemCount = read_long();
			org.omg.CORBA.StructMember[] exceptMembers = new org.omg.CORBA.StructMember[exceptMemCount];

			for (int i = 0; i < exceptMemCount; i++) {
				String memberName = read_string();
				org.omg.CORBA.TypeCode type = read_TypeCode();

				exceptMembers[i] = new org.omg.CORBA.StructMember(memberName,
						type, null);
			}

			return orb.create_exception_tc(exceptId, exceptName, exceptMembers);
			// break;

		case TCKind._tk_union:
			String unionId = read_string();
			String unionName = read_string();
			org.omg.CORBA.TypeCode unionTypeCode = read_TypeCode();
			int defaultIndex = read_long();
			int unionMemCount = read_long();
			org.omg.CORBA.UnionMember[] unionMembers = new org.omg.CORBA.UnionMember[unionMemCount];

			for (int i = 0; i < unionMemCount; i++) {
				// Unknow what to do
				// throw new org.omg.CORBA.NO_IMPLEMENT();
				org.omg.CORBA.Any theLabel = read_any();
				String memberName = read_string();
				org.omg.CORBA.TypeCode type = read_TypeCode();

				unionMembers[i] = new org.omg.CORBA.UnionMember(memberName,
						theLabel, type, null);
			}

			return orb.create_union_tc(unionId, unionName, unionTypeCode,
					unionMembers);
			// break;

		case TCKind._tk_alias:
			String aliasId = read_string();
			String aliasName = read_string();

			org.omg.CORBA.TypeCode aliasType = read_TypeCode();

			return orb.create_alias_tc(aliasId, aliasName, aliasType);
			// break;

		case TCKind._tk_abstract_interface:
			String abstractInfId = read_string();
			String abstractInfName = read_string();

			return orb.create_abstract_interface_tc(abstractInfId,
					abstractInfName);
			// break;

		case TCKind._tk_sequence:
			int seqLength = read_long();
			org.omg.CORBA.TypeCode seqElemType = read_TypeCode();

			return orb.create_sequence_tc(seqLength, seqElemType);
			// break;

		case TCKind._tk_string:
			int stringBound = read_long();

			return orb.create_string_tc(stringBound);
			// break;
		case TCKind._tk_array:
			org.omg.CORBA.TypeCode arrayType = read_TypeCode();
			int arrayCount = read_long();

			orb.create_array_tc(arrayCount, arrayType);
			break;
		case TCKind._tk_wstring:
			int wstringBound = read_long();

			return orb.create_string_tc(wstringBound);
			// break;
		case TCKind._tk_fixed:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// break;
		case TCKind._tk_value:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// break;
		case TCKind._tk_value_box:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// break;
		case TCKind._tk_native:
			String nativeId = read_string();
			String nativeName = read_string();

			orb.create_native_tc(nativeId, nativeName);
			// break;
			// case TCKind._tk_local_interface:
			// String localId = read_string();
			// String localName = read_string();
			//
			// orb.create_local_interface_tc(localId, localName);
			// break;
		default:
			System.out.println("CDRInputStream Invalid TCKind code: " + kind);
			return null;

		}

		System.out.println("Invalid TCKind code: " + kind);
		return null;
	}

	public org.omg.CORBA.Context read_Context() {
		// do nothing
		return null;
	}

	public org.omg.CORBA.Principal read_Principal() {
		org.omg.CORBA.Principal p = new org.omg.CORBA.Principal();
		byte[] b = new byte[read_ulong()];
		read_octet_array(b, 0, b.length);
		p.name(b);
		return p;
	}

	public java.math.BigDecimal read_fixed() {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	public java.math.BigDecimal read_fixed(short digits, short scale) {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	public java.io.Serializable read_value() {
		String[] id = null;
		byte[] buf = null;
		String[] codeBase = null;
		java.io.Serializable ser = null;
		int size = 0;
		java.lang.Class clz;
		java.lang.Object object;

		alignment(4);
		indirection = nextByteToRead;

		int tag = read_long();

		if (tag == 0xffffffff) {
			int index = read_long();
			index = index + nextByteToRead - 4;

			ser = getValueFromList(index);

			if (ser == null) {
				mark();
				nextByteToRead = index;
				ser = read_value();
				reset();

				if (ser == null) {
					throw new org.omg.CORBA.MARSHAL("Invalid indirection");
				}
			}

			return ser;
		}

		if (tag == 0) {
			return null;
		}

		if (!((tag >= 0x7fffff00) && (tag <= 0x7fffffff))) {
			throw new org.omg.CORBA.MARSHAL("Invalid value tag");
		}

		if ((tag & 0x00000001) == 1) {
			codeBase = readValueCodeBase();
		}

		if ((tag & 0x00000006) == 2) {
			id = new String[1];

			id[0] = readValueRepId();
		}

		if ((tag & 0x00000006) == 6) {
			id = readValueRepIdList();
		}

		size = 0;

		if ((tag & 0x00000008) == 8) {
			size = read_long();
			buf = new byte[size];

			read_octet_array(buf, 0, size);
		}

		org.omg.CORBA.portable.ValueFactory factory = null;
		int i = 0;

		try {
			while (true) {
				if (factory == null) {
					factory = orb.lookup_value_factory(id[i]);
				}

				if (factory == null) {
					try {
						factory = loadFactory(id[i], codeBase);
					} catch (org.omg.CORBA.MARSHAL ex) {
					}

					i++;

					if (i == id.length) {
						break;
					}

				} else {
					break;
				}
			}
		} catch (java.lang.Exception ex) {
			ex.printStackTrace();
			throw new org.omg.CORBA.MARSHAL();
		}

		if (factory == null) {
			throw new org.omg.CORBA.MARSHAL();
		}

		if (size == 0) {
			ser = factory.read_value(this);
		} else {
			CDRInputStream input = new CDRInputStream(getBuffer());
			input.setLittleEndian(getLittleEndian());
			ser = factory.read_value(input);
		}

		if ((tag & 0x00000008) == 8) {
			read_long();
		}

		putValueToList(indirection, ser);
		return ser;
	}

	public java.io.Serializable read_value(java.lang.String rep_id) {
		String[] id = null;
		byte[] buf = null;
		String[] codeBase = null;
		java.io.Serializable ser = null;
		int size = 0;
		java.lang.Class clz;
		java.lang.Object object;

		alignment(4);
		indirection = nextByteToRead;

		int tag = read_long();

		if (tag == 0xffffffff) {
			int index = read_long();
			index = index + nextByteToRead - 4;

			ser = getValueFromList(index);

			if (ser == null) {
				mark();
				nextByteToRead = index;
				ser = read_value();
				reset();

				if (ser == null) {
					throw new org.omg.CORBA.MARSHAL("Invalid indirection");
				}
			}

			return ser;
		}

		if (tag == 0) {
			return null;
		}

		if (!((tag >= 0x7fffff00) && (tag <= 0x7fffffff))) {
			throw new org.omg.CORBA.MARSHAL("Invalid value tag");
		}

		if ((tag & 0x00000001) == 1) {
			codeBase = readValueCodeBase();
		}

		if ((tag & 0x00000006) == 2) {
			id = new String[1];

			id[0] = readValueRepId();
		}

		if ((tag & 0x00000006) == 6) {
			id = readValueRepIdList();
		}

		boolean found = false;

		for (int i = 0; i < id.length; i++) {
			if (id[i].equals(rep_id)) {
				found = true;
			}
		}

		if (!found) {
			throw new org.omg.CORBA.BAD_PARAM();
		}

		size = 0;
		if ((tag & 0x00000008) == 8) {
			size = read_long();
			buf = new byte[size];
			read_octet_array(buf, 0, size);
		}

		org.omg.CORBA.portable.ValueFactory factory = null;
		int i = 0;
		try {
			while (true) {
				if (factory == null) {
					factory = orb.lookup_value_factory(id[i]);
				}

				if (factory == null) {
					try {
						factory = loadFactory(id[i], codeBase);
					} catch (org.omg.CORBA.MARSHAL ex) {
					}

					i++;

					if (i == id.length) {
						break;
					}
				} else {
					break;
				}
			}
		} catch (java.lang.Exception ex) {
			ex.printStackTrace();
			throw new org.omg.CORBA.MARSHAL();
		}

		if (factory == null) {
			throw new org.omg.CORBA.MARSHAL();
		}

		if (size == 0) {
			ser = factory.read_value(this);
		} else {
			CDRInputStream input = new CDRInputStream(getBuffer());
			input.setLittleEndian(getLittleEndian());
			ser = factory.read_value(input);
		}

		if ((tag & 0x00000008) == 8) {
			read_long();
		}

		putValueToList(indirection, ser);
		return ser;

	}

	public java.io.Serializable read_value(java.lang.Class clz) {
		java.lang.Object value = null;

		try {
			value = clz.newInstance();
		} catch (InstantiationException ex) {
			throw new org.omg.CORBA.BAD_PARAM(
					"Unable to create an instance of " + clz.getName());
		} catch (IllegalAccessException ex) {
			throw new org.omg.CORBA.BAD_PARAM("Unable to access to "
					+ clz.getName());
		}

		if (value instanceof java.io.Serializable) {
			return read_value((java.io.Serializable) value);
		}

		throw new org.omg.CORBA.BAD_PARAM();
	}

	public java.io.Serializable read_value(
			org.omg.CORBA.portable.BoxedValueHelper factory) {
		String[] id;
		String[] codeBase = null;

		alignment(4);
		indirection = nextByteToRead;

		int tag = read_long();

		if (tag == 0xffffffff) {
			int index = read_long();
			index = index + nextByteToRead - 4;

			java.io.Serializable ser = getValueFromList(index);

			if (ser == null) {

				mark();
				nextByteToRead = index;
				ser = read_value();
				reset();

				if (ser == null) {
					throw new org.omg.CORBA.MARSHAL("Invalid indirection");
				}
			}

			return ser;
		}

		if (tag == 0) {
			return null;
		}

		if (!((tag >= 0x7fffff00) && (tag <= 0x7fffffff))) {
			throw new org.omg.CORBA.MARSHAL("Invalid value tag");
		}

		if ((tag & 0x00000001) == 1) {
			codeBase = readValueCodeBase();
		}

		if ((tag & 0x00000006) == 2) {
			id = new String[1];

			id[0] = readValueRepId();

			if (!id[0].equals(factory.get_id())) {
				throw new org.omg.CORBA.MARSHAL();
			}
		}

		java.io.Serializable ret = factory.read_value(this);
		putValueToList(indirection, ret);
		return ret;
	}

	public java.io.Serializable read_value(java.io.Serializable value) {

		if (value instanceof org.omg.CORBA.portable.CustomValue) {
			org.omg.CORBA.portable.CustomValue customValue;
			customValue = (org.omg.CORBA.portable.CustomValue) value;
			org.omg.CORBA.DataInputStream inputStream = new org.huihoo.orbas.orb.InputStream(
					this);
			customValue.unmarshal(inputStream);

			return value;
		} else {
			if (value instanceof org.omg.CORBA.portable.StreamableValue) {
				// resolve circulation graph of valuetype
				putValueToList(indirection, value);
				((org.omg.CORBA.portable.StreamableValue) (value))._read(this);

				return value;
			}
		}

		throw new org.omg.CORBA.BAD_PARAM();

	}

	public java.lang.Object read_abstract_interface() {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	public java.lang.Object read_abstract_interface(java.lang.Class clz) {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	protected org.omg.CORBA.portable.ValueFactory loadFactory(String id,
			String[] codeBase) {
		org.omg.CORBA.portable.ValueFactory factory = null;

		if (id.startsWith("IDL")) {
			String baseName = id.substring(4, id.lastIndexOf(":"));

			baseName = baseName.replace('/', '.');
			String factoryName = baseName + "DefaultFactory";

			// load factory class
			try {
				factory = (org.omg.CORBA.portable.ValueFactory) (Class
						.forName(factoryName).newInstance());
			} catch (ClassNotFoundException ec) {
				System.out.println(ec.toString());
			} catch (java.lang.InstantiationException ei) {
				System.out.println(ei.toString());
			} catch (java.lang.IllegalAccessException eill) {
				System.out.println(eill.toString());
			}
		}

		return factory;

	}

	protected java.io.Serializable getValueFromList(int index) {
		java.util.Enumeration en = valueList.keys();

		java.lang.Integer val = null;

		while (en.hasMoreElements()) {
			val = (java.lang.Integer) en.nextElement();

			if (val.intValue() == index)
				return (java.io.Serializable) valueList.get(val);
		}

		return null;
	}

	protected void putValueToList(int index, java.io.Serializable value) {
		valueList.put(new Integer(index), value);
	}

	void alignment(int alignSize) {
		int boundary = (nextByteToRead) % alignSize;

		if (boundary != 0)
			nextByteToRead += alignSize - boundary; // skip padding
	}

	protected String[] readValueCodeBase() {
		String[] codeBase = null;

		mark();
		String url;
		int tag = read_long();

		if (tag == 0xffffffff) {
			// read indirection string
			int index = read_long();

			// old mark is lost
			mark();

			nextByteToRead = nextByteToRead + index - 4;
			url = read_string();

			reset();
		} else {
			reset();
			url = read_string();
		}

		java.util.StringTokenizer tokens = new java.util.StringTokenizer(url);
		codeBase = new String[tokens.countTokens()];

		int i = 0;

		while (tokens.hasMoreTokens()) {
			codeBase[i] = tokens.nextToken();
			i++;
		}

		return codeBase;
	}

	protected String readValueRepId() {
		String id = null;

		mark();
		int tag = read_long();

		if (tag == 0xffffffff) {
			// read indirection string
			int index = read_long();

			// old mark is lost
			mark();

			nextByteToRead = nextByteToRead + index - 4;
			id = read_string();

			reset();
		} else {
			reset();
			id = read_string();
		}

		return id;
	}

	protected String[] readValueRepIdList() {
		String[] id = null;

		int length = read_long();
		id = new String[read_long()];

		for (int i = 0; i < length; i++) {
			id[i] = readValueRepId();
		}

		return id;
	}

	public void setLittleEndian(boolean endian) {
		littleEndian = endian;
	}

	public boolean getLittleEndian() {
		return littleEndian;
	}

	void skip(int n) {
		nextByteToRead += n;
	}

	public boolean isEnd() {
		return nextByteToRead >= bufferLength;
	}

	public byte[] getBuffer() {
		return buffer;
	}
}
